/*
 * REFTimerService.cpp
 *
 *  Created on: Jun 12, 2013
 *      Author: Lucifer
 */

#include "../pch.h"
#include "events.h"
#include "REFEventReactor.h"
#include "REFTimerService.h"

namespace BFX
{

REFTimerContext::REFTimerContext(REFTimer* pTimer, TimerCallback callback)
{
	BFX_ASSERT(pTimer != NULL);

	m_pTimer = pTimer;
	m_callback = callback;
}

REFTimer* REFTimerContext::GetTimer() const
{
	return m_pTimer;
}

/**
 * Initializes a new instance of the BFX::REFTimerQueue.
 */
REFTimerQueue::REFTimerQueue()
{
	m_pTimers = NULL;
}

/**
 * Determine whether there are no timers in the queue.
 * @return True if the queue is empty; otherwise false.
 */
bool REFTimerQueue::IsEmpty() const
{
	return (m_pTimers == NULL);
}

/**
 * Enqueue a new timer to the queue.
 * @param pContext
 * @return True if the timer is earliest in the queue, in which case the reactor's event demultiplexing function
 * 		call may need to be interrupted and restarted; otherwise false.
 */
bool REFTimerQueue::EnqueueContext(REFTimerContext* pContext)
{
	REFTimer* pTimer = pContext->GetTimer();

	// register the timer if not exist in the queue.

	// enqueue the timer object.
	if (pTimer->m_pPrev == NULL && pTimer != m_pTimers)
	{
		// NOTE: BFX::Timer::MaxValue() means infinity expiration.
		if (pTimer->GetAbsoluteExpiration() >= Time::MaxValue())
		{
			// no heap entry is required for timers that never expire.
			pTimer->m_nSortIndex = INT_MAX;
		}
		else
		{
			// put the new timer at the correct position in the heap.
			pTimer->m_nSortIndex = m_sortQueue.GetSize();
			m_sortQueue.Add(pTimer);
			MoveUpTimers(m_sortQueue.GetSize() - 1);
		}

		// add the timer to the first position of the linked active timers.
		pTimer->m_pNext = m_pTimers;
		if (m_pTimers)
			m_pTimers->m_pPrev = pTimer;
		m_pTimers = pTimer;
	}

	// enqueue the individual timer context.
	pTimer->m_pContextQueue->Enqueue(pContext);

	// interrupt only if newly added timer is first to expire.
	return (pTimer->m_nSortIndex == 0 && pTimer->m_pContextQueue->Peek() == pContext);
}

/**
 * Dequeue all timers.
 * @param pContexts
 */
void REFTimerQueue::DequeueAllContexts(REFEventContextList* pContexts)
{
	// TODO: xxxx
}

/**
 * Dequeue all ready timers.
 * @param pContexts
 */
void REFTimerQueue::DequeueReadyContexts(REFEventContextList* pContexts)
{
	// TODO: xxxx
}

// Move the item at the given index up the array to its correct position.
void REFTimerQueue::MoveUpTimers(int nIndex)
{
	int nParent = (nIndex - 1) / 2;
	while (nIndex > 0 && m_sortQueue[nIndex]->GetAbsoluteExpiration() < m_sortQueue[nParent]->GetAbsoluteExpiration())
	{
		REFTimer* pTimer = m_sortQueue[nIndex];
		m_sortQueue[nIndex] = m_sortQueue[nParent];
		m_sortQueue[nIndex]->m_nSortIndex = nIndex;
		m_sortQueue[nParent] = pTimer;
		m_sortQueue[nParent]->m_nSortIndex = nParent;

		nIndex = nParent;
		nParent = (nIndex - 1) / 2;
	}
}

// Move the time at the given index down the array to its correct position.
void REFTimerQueue::MoveDownTimers(int nIndex)
{
	int nChild = nIndex * 2 + 1;
	while (nChild < m_sortQueue.GetSize())
	{
		int nMinChild =
				(((nChild + 1) == m_sortQueue.GetSize())
						|| (m_sortQueue[nChild]->GetAbsoluteExpiration()
								< m_sortQueue[nMinChild]->GetAbsoluteExpiration())) ? nChild : nChild + 1;
		if (m_sortQueue[nIndex]->GetAbsoluteExpiration() < m_sortQueue[nMinChild]->GetAbsoluteExpiration())
			break;

		REFTimer* pTimer = m_sortQueue[nIndex];
		m_sortQueue[nIndex] = m_sortQueue[nMinChild];
		m_sortQueue[nIndex]->m_nSortIndex = nIndex;
		m_sortQueue[nMinChild] = pTimer;
		m_sortQueue[nMinChild]->m_nSortIndex = nMinChild;

		nIndex = nMinChild;
		nChild = nIndex * 2 + 1;
	}
}

////////////////////////////////////////////////////////////////////////////////

/**
 * Initializes a new instance of BFX::REFTimerService.
 * @param pDispatcher The own event dispatcher.
 */
REFTimerService::REFTimerService(REFEventDispatcher* pDispatcher)
	: REFServiceBase(pDispatcher)
{
	BFX_REQUIRE(pDispatcher != NULL);

	m_pReactor = pDispatcher->GetServiceRegistry()->CreateOrGetService<REFEventReactor>();

	m_pTimerQueue = new REFTimerQueue();
	m_pReactor->Restart();
	m_pReactor->AddTimerQueue(m_pTimerQueue);
}

/**
 * Destroys the BFX::REFTimerService instance.
 */
REFTimerService::~REFTimerService()
{
	m_pReactor->RemoveTimerQueue(m_pTimerQueue);
}

/**
 * Shutdown this service.
 */
void REFTimerService::Shutdown()
{
}

/**
 * Cancel any asynchronous wait operations associated with the timer.
 * @param pTimer
 * @return Number of operations were canceled.
 */
int REFTimerService::Cancel(REFTimer* pTimer)
{
	BFX_REQUIRE(pTimer != NULL);

	m_pReactor->CancelTimer(m_pTimerQueue, pTimer);
	return 0;
}

/**
 * Perform a blocking wait on the timer.
 * @param pTimer
 */
void REFTimerService::Wait(REFTimer* pTimer)
{

}

/**
 * Starts an asynchronous wait on the timer.
 * @param pTimer
 * @param callback
 */
void REFTimerService::WaitAsync(REFTimer* pTimer, TimerCallback callback)
{
	BFX_REQUIRE(pTimer != NULL);

	REF<REFTimerContext> pContext = new REFTimerContext(pTimer, callback);
	m_pReactor->ScheduleTimer(m_pTimerQueue, pContext);
}

} /* namespace BFX */
